package com.laker.autotest.desktop.dev;

import cn.hutool.core.thread.ThreadUtil;
import com.github.kwhat.jnativehook.GlobalScreen;
import com.github.kwhat.jnativehook.keyboard.NativeKeyEvent;
import com.github.kwhat.jnativehook.keyboard.NativeKeyListener;
import com.github.kwhat.jnativehook.mouse.*;
import com.laker.autotest.desktop.utils.ObjectUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * GUI 操作录制与回放工具类
 * 该类实现了对用户鼠标和键盘操作的录制与回放功能，通过图形用户界面提供交互操作。
 */
@Slf4j
public class GUIRecorder {
    // 存储录制操作的文件路径
    private static final String RECORD_FILE = "recorded_actions.txt";
    // 存储录制操作的列表
    private static final List<String> actions = new ArrayList<>();
    // 录制状态标识
    private static boolean isRecording = false;
    // 录制开始时间
    private static long startTime = 0;
    // 全局事件监听器
    private static final GlobalNativeHookListener listener = new GlobalNativeHookListener();

    public static void main(String[] args) {
        try {
            // 注册全局钩子以监听鼠标和键盘事件
            GlobalScreen.registerNativeHook();
            // 在事件调度线程中创建并显示 GUI
            SwingUtilities.invokeLater(GUIRecorder::createGUI);
        } catch (Exception e) {
            log.error("启动 GUI 录制工具失败", e);
        }
    }

    /**
     * 创建 GUI 界面
     * 该方法创建了一个简单的图形用户界面，包含开始、停止和回放按钮。
     */
    private static void createGUI() {
        JFrame frame = new JFrame("GUI 操作录制与回放工具");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(480, 280);
        frame.setLocationRelativeTo(null);
        // 商务风格渐变背景（深蓝到浅灰）
        JPanel mainPanel = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g;
                Color color1 = new Color(36, 49, 66); // 深蓝
                Color color2 = new Color(245, 247, 250); // 浅灰
                GradientPaint gp = new GradientPaint(0, 0, color1, 0, getHeight(), color2);
                g2d.setPaint(gp);
                g2d.fillRect(0, 0, getWidth(), getHeight());
            }
        };
        mainPanel.setLayout(new BorderLayout());
        mainPanel.setBorder(BorderFactory.createEmptyBorder(24, 32, 24, 32));

        JLabel title = new JLabel("GUI 操作录制与回放工具", SwingConstants.CENTER);
        title.setFont(new Font(getSystemFont(), Font.BOLD, 22));
        title.setForeground(new Color(36, 49, 66));
        mainPanel.add(title, BorderLayout.NORTH);

        JPanel btnPanel = new JPanel();
        btnPanel.setOpaque(false);
        btnPanel.setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(0, 18, 0, 18);
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.NONE;

        // 更商务的按钮配色：深灰、银灰、藏蓝
        JButton startButton = createModernButton("开始录制", new Color(52, 73, 94), new Color(44, 62, 80)); // 深灰蓝
        JButton stopButton = createModernButton("停止录制", new Color(149, 165, 166), new Color(127, 140, 141)); // 银灰
        JButton playButton = createModernButton("回放操作", new Color(35, 57, 91), new Color(25, 42, 71)); // 藏蓝
        stopButton.setEnabled(false);
        startButton.addActionListener(e -> startRecording(startButton, stopButton, playButton));
        stopButton.addActionListener(e -> stopRecording(startButton, stopButton, playButton));
        playButton.addActionListener(e -> startPlayback(startButton, stopButton, playButton));
        gbc.gridx = 0;
        btnPanel.add(startButton, gbc);
        gbc.gridx = 1;
        btnPanel.add(stopButton, gbc);
        gbc.gridx = 2;
        btnPanel.add(playButton, gbc);
        mainPanel.add(btnPanel, BorderLayout.CENTER);

        JLabel tip = new JLabel("支持 Windows/macOS，录制后可回放操作", SwingConstants.CENTER);
        tip.setFont(new Font(getSystemFont(), Font.PLAIN, 14));
        tip.setForeground(new Color(120, 130, 150));
        mainPanel.add(tip, BorderLayout.SOUTH);

        frame.setContentPane(mainPanel);
        frame.setVisible(true);
        log.info("GUI Started.");
    }

    // 商务风格按钮，深色主色+hover高亮+圆角+阴影
    private static JButton createModernButton(String text, Color color1, Color color2) {
        JButton button = new JButton(text) {
            @Override
            protected void paintComponent(Graphics g) {
                Graphics2D g2 = (Graphics2D) g.create();
                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                // 阴影
                g2.setColor(new Color(0, 0, 0, 30));
                g2.fillRoundRect(3, 4, getWidth() - 6, getHeight() - 4, 18, 18);
                Color grad1 = getModel().isRollover() ? color1.brighter() : color1;
                Color grad2 = getModel().isRollover() ? color2.brighter() : color2;
                g2.setPaint(new GradientPaint(0, 0, grad1, 0, getHeight(), grad2));
                g2.fillRoundRect(0, 0, getWidth(), getHeight() - 2, 18, 18);
                g2.dispose();
                super.paintComponent(g);
            }

            @Override
            protected void paintBorder(Graphics g) {
                g.setColor(new Color(220, 220, 220));
                g.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 3, 18, 18);
            }
        };
        button.setForeground(Color.WHITE);
        button.setFont(new Font(getSystemFont(), Font.BOLD, 16));
        button.setBorderPainted(false);
        button.setOpaque(false);
        button.setContentAreaFilled(false);
        button.setFocusPainted(false);
        button.setPreferredSize(new Dimension(128, 44));
        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        return button;
    }

    // 获取系统默认字体，兼容mac/windows
    private static String getSystemFont() {
        String os = System.getProperty("os.name").toLowerCase();
        if (os.contains("mac")) {
            return "PingFang SC";
        } else {
            return "Microsoft YaHei";
        }
    }

    // 开始录制
    private static void startRecording(JButton startButton, JButton stopButton, JButton playButton) {
        if (isRecording) {
            log.info("Recording is already in progress.");
            return;
        }
        startTime = 0; // 只在事件监听器第一次事件时初始化
        // 监听键盘和鼠标事件
        GlobalScreen.addNativeKeyListener(listener);
        GlobalScreen.addNativeMouseListener(listener);
        GlobalScreen.addNativeMouseWheelListener(listener);
        GlobalScreen.addNativeMouseMotionListener(listener);
        actions.clear();
        // 清空文件内容
        try (PrintWriter writer = new PrintWriter(RECORD_FILE)) {
            writer.print("");
        } catch (FileNotFoundException e) {
            log.error("Failed to clear file", e);
        }
        isRecording = true;
        log.info("Recording started...");
        // 更新按钮状态
        startButton.setEnabled(false);
        stopButton.setEnabled(true);
        playButton.setEnabled(false);
    }

    // 停止录制
    private static void stopRecording(JButton startButton, JButton stopButton, JButton playButton) {
        if (!isRecording) {///123/123/123/123
            log.info("No recording in progress.");
            return;
        }

        GlobalScreen.removeNativeKeyListener(listener);
        GlobalScreen.removeNativeMouseListener(listener);
        GlobalScreen.removeNativeMouseWheelListener(listener);
        GlobalScreen.removeNativeMouseMotionListener(listener);

        isRecording = false;
        log.info("Recording stopped.");
        saveActionsToFile();
        actions.clear();
        // 更新按钮状态
        startButton.setEnabled(true);
        stopButton.setEnabled(false);
        playButton.setEnabled(true);
    }

    // 保存录制的操作
    private static void saveActionsToFile() {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(RECORD_FILE))) {
            for (String action : actions) {
                writer.write(action);
                writer.newLine();
            }
            log.info("Actions saved to file: " + RECORD_FILE);
        } catch (IOException e) {
            log.error("Failed to save actions", e);
        }
    }

    // 回放录制的操作
    private static void startPlayback(JButton startButton, JButton stopButton, JButton playButton) {
        // 更新按钮状态
        startButton.setEnabled(false);
        stopButton.setEnabled(false);
        playButton.setEnabled(false);
        ThreadUtil.execute(() -> {
            try {
                replay(RECORD_FILE);
            } catch (Exception e) {
                log.error("Playback failed", e);
            } finally {
                // 回放完成后，允许重新录制
                SwingUtilities.invokeLater(() -> {
                    startButton.setEnabled(true);
                    stopButton.setEnabled(false);
                    playButton.setEnabled(true);
                    JOptionPane.showMessageDialog(null, "回放完成", "Info", JOptionPane.INFORMATION_MESSAGE);
                });
            }
        });

    }

    public static void replay() {
        replay(RECORD_FILE);
    }

    // 回放操作
    @SneakyThrows
    public static void replay(String file) {
        List<String> loadedActions = loadActionsFromFile(file);
        PlaybackStrategy strategy;
        String os = System.getProperty("os.name").toLowerCase();
        if (os.contains("mac")) {
            strategy = new JnativehookPlaybackStrategy();
        } else {
            strategy = new JnativehookPlaybackStrategy();
        }
        strategy.replay(loadedActions);
    }

    // 从文件加载操作
    private static List<String> loadActionsFromFile(String file) {
        List<String> loadedActions = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = reader.readLine()) != null) {
                loadedActions.add(line);
            }
        } catch (IOException e) {
            log.error("Failed to load actions", e);
        }
        return loadedActions;
    }

    static class GlobalNativeHookListener implements NativeMouseMotionListener, NativeMouseListener, NativeMouseWheelListener, NativeKeyListener {


        @Override
        public void nativeKeyPressed(NativeKeyEvent nativeEvent) {
            long time = nativeEvent.getWhen() / 1_000_000; // 纳秒转毫秒
            long delay;
            if (startTime == 0) {
                startTime = time;
                delay = 1;
            } else {
                delay = time - startTime;
                startTime = time;
            }
            log.info("[Record] nativeKeyPressed: time={}, delay={}, event={}", time, delay, ObjectUtil.ObjectToString(nativeEvent));
            actions.add(delay + "#" + ObjectUtil.ObjectToString(nativeEvent));
        }

        @Override
        public void nativeKeyReleased(NativeKeyEvent nativeEvent) {
            long time = nativeEvent.getWhen() / 1_000_000;
            long delay;
            if (startTime == 0) {
                startTime = time;
                delay = 1;
            } else {
                delay = time - startTime;
                startTime = time;
            }
            log.info("[Record] nativeKeyReleased: time={}, delay={}, event={}", time, delay, ObjectUtil.ObjectToString(nativeEvent));
            actions.add(delay + "#" + ObjectUtil.ObjectToString(nativeEvent));
        }

        @Override
        public void nativeMousePressed(NativeMouseEvent nativeEvent) {
            long time = nativeEvent.getWhen() / 1_000_000;
            long delay;
            if (startTime == 0) {
                startTime = time;
                delay = 1;
            } else {
                delay = time - startTime;
                startTime = time;
            }
            log.info("[Record] nativeMousePressed: time={}, delay={}, event={}", time, delay, ObjectUtil.ObjectToString(nativeEvent));
            actions.add(delay + "#" + ObjectUtil.ObjectToString(nativeEvent));
        }

        @Override
        public void nativeMouseReleased(NativeMouseEvent nativeEvent) {
            long time = nativeEvent.getWhen() / 1_000_000;
            long delay;
            if (startTime == 0) {
                startTime = time;
                delay = 1;
            } else {
                delay = time - startTime;
                startTime = time;
            }
            log.info("[Record] nativeMouseReleased: time={}, delay={}, event={}", time, delay, ObjectUtil.ObjectToString(nativeEvent));
            actions.add(delay + "#" + ObjectUtil.ObjectToString(nativeEvent));
        }

        @Override
        public void nativeMouseMoved(NativeMouseEvent nativeEvent) {
            long time = nativeEvent.getWhen() / 1_000_000;
            long delay;
            if (startTime == 0) {
                startTime = time;
                delay = 1;
            } else {
                delay = time - startTime;
                startTime = time;
            }
            log.info("[Record] nativeMouseMoved: time={}, delay={}, event={}", time, delay, ObjectUtil.ObjectToString(nativeEvent));
            actions.add(delay + "#" + ObjectUtil.ObjectToString(nativeEvent));
        }

        @Override
        public void nativeMouseDragged(NativeMouseEvent nativeEvent) {
            long time = nativeEvent.getWhen() / 1_000_000;
            long delay;
            if (startTime == 0) {
                startTime = time;
                delay = 1;
            } else {
                delay = time - startTime;
                startTime = time;
            }
            log.info("[Record] nativeMouseDragged: time={}, delay={}, event={}", time, delay, ObjectUtil.ObjectToString(nativeEvent));
            actions.add(delay + "#" + ObjectUtil.ObjectToString(nativeEvent));
        }

        @Override
        public void nativeMouseWheelMoved(NativeMouseWheelEvent nativeEvent) {
            long time = nativeEvent.getWhen() / 1_000_000;
            long delay;
            if (startTime == 0) {
                startTime = time;
                delay = 1;
            } else {
                delay = time - startTime;
                startTime = time;
            }
            log.info("[Record] nativeMouseWheelMoved: time={}, delay={}, event={}", time, delay, ObjectUtil.ObjectToString(nativeEvent));
            actions.add(delay + "#" + ObjectUtil.ObjectToString(nativeEvent));
        }
    }
}
